{
 "cells": [
  {
   "attachments": {},
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "**问题：**\n",
    "假设你正在为一个电影推荐系统设计一个简单的KNN算法。我们有以下一些用户的电影评分数据，数据由两个特征组成：用户对电影A和电影B的评分，分别在1-5之间。用户的标签（电影类型偏好）是动作片（标签0）或者是喜剧片（标签1）。我们有一个新用户，他给电影A评分为3，电影B评分为4。请问这个用户可能偏好哪种类型的电影？\n",
    "\n",
    "**数据：**\n",
    "\n",
    "| 用户   | 电影A评分 | 电影B评分 | 偏好类型 |\n",
    "| ------ | --------- | --------- | -------- |\n",
    "| 用户1  | 5         | 1         | 动作片   |\n",
    "| 用户2  | 4         | 2         | 动作片   |\n",
    "| 用户3  | 2         | 5         | 喜剧片   |\n",
    "| 用户4  | 1         | 4         | 喜剧片   |\n",
    "| 用户5  | 3         | 2         | 动作片   |\n",
    "| 用户6  | 2         | 5         | 喜剧片   |\n",
    "\n",
    "你需要做以下步骤：\n",
    "1. 构造数据\n",
    "2. 创建KNN模型\n",
    "3. 使用数据训练模型\n",
    "4. 预测新用户的喜好"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 0. 引入核心包"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "from sklearn.neighbors import KNeighborsClassifier"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 1. X, y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "X = np.array([[5, 1], [4, 2], [2, 5], [1, 4], [3, 2], [2, 5]])\n",
    "y = np.array([0, 0, 1, 1, 0, 1])  # 0表示动作片，1表示喜剧片"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 2. 创建 KNN 模型\n",
    "k = 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "k = 1\n",
    "knn = KNeighborsClassifier(n_neighbors=k)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 3. 训练模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style>#sk-container-id-1 {\n",
       "  /* Definition of color scheme common for light and dark mode */\n",
       "  --sklearn-color-text: #000;\n",
       "  --sklearn-color-text-muted: #666;\n",
       "  --sklearn-color-line: gray;\n",
       "  /* Definition of color scheme for unfitted estimators */\n",
       "  --sklearn-color-unfitted-level-0: #fff5e6;\n",
       "  --sklearn-color-unfitted-level-1: #f6e4d2;\n",
       "  --sklearn-color-unfitted-level-2: #ffe0b3;\n",
       "  --sklearn-color-unfitted-level-3: chocolate;\n",
       "  /* Definition of color scheme for fitted estimators */\n",
       "  --sklearn-color-fitted-level-0: #f0f8ff;\n",
       "  --sklearn-color-fitted-level-1: #d4ebff;\n",
       "  --sklearn-color-fitted-level-2: #b3dbfd;\n",
       "  --sklearn-color-fitted-level-3: cornflowerblue;\n",
       "\n",
       "  /* Specific color for light theme */\n",
       "  --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
       "  --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, white)));\n",
       "  --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
       "  --sklearn-color-icon: #696969;\n",
       "\n",
       "  @media (prefers-color-scheme: dark) {\n",
       "    /* Redefinition of color scheme for dark theme */\n",
       "    --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
       "    --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, #111)));\n",
       "    --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
       "    --sklearn-color-icon: #878787;\n",
       "  }\n",
       "}\n",
       "\n",
       "#sk-container-id-1 {\n",
       "  color: var(--sklearn-color-text);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 pre {\n",
       "  padding: 0;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 input.sk-hidden--visually {\n",
       "  border: 0;\n",
       "  clip: rect(1px 1px 1px 1px);\n",
       "  clip: rect(1px, 1px, 1px, 1px);\n",
       "  height: 1px;\n",
       "  margin: -1px;\n",
       "  overflow: hidden;\n",
       "  padding: 0;\n",
       "  position: absolute;\n",
       "  width: 1px;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-dashed-wrapped {\n",
       "  border: 1px dashed var(--sklearn-color-line);\n",
       "  margin: 0 0.4em 0.5em 0.4em;\n",
       "  box-sizing: border-box;\n",
       "  padding-bottom: 0.4em;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-container {\n",
       "  /* jupyter's `normalize.less` sets `[hidden] { display: none; }`\n",
       "     but bootstrap.min.css set `[hidden] { display: none !important; }`\n",
       "     so we also need the `!important` here to be able to override the\n",
       "     default hidden behavior on the sphinx rendered scikit-learn.org.\n",
       "     See: https://github.com/scikit-learn/scikit-learn/issues/21755 */\n",
       "  display: inline-block !important;\n",
       "  position: relative;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-text-repr-fallback {\n",
       "  display: none;\n",
       "}\n",
       "\n",
       "div.sk-parallel-item,\n",
       "div.sk-serial,\n",
       "div.sk-item {\n",
       "  /* draw centered vertical line to link estimators */\n",
       "  background-image: linear-gradient(var(--sklearn-color-text-on-default-background), var(--sklearn-color-text-on-default-background));\n",
       "  background-size: 2px 100%;\n",
       "  background-repeat: no-repeat;\n",
       "  background-position: center center;\n",
       "}\n",
       "\n",
       "/* Parallel-specific style estimator block */\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item::after {\n",
       "  content: \"\";\n",
       "  width: 100%;\n",
       "  border-bottom: 2px solid var(--sklearn-color-text-on-default-background);\n",
       "  flex-grow: 1;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel {\n",
       "  display: flex;\n",
       "  align-items: stretch;\n",
       "  justify-content: center;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  position: relative;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item {\n",
       "  display: flex;\n",
       "  flex-direction: column;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item:first-child::after {\n",
       "  align-self: flex-end;\n",
       "  width: 50%;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item:last-child::after {\n",
       "  align-self: flex-start;\n",
       "  width: 50%;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item:only-child::after {\n",
       "  width: 0;\n",
       "}\n",
       "\n",
       "/* Serial-specific style estimator block */\n",
       "\n",
       "#sk-container-id-1 div.sk-serial {\n",
       "  display: flex;\n",
       "  flex-direction: column;\n",
       "  align-items: center;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  padding-right: 1em;\n",
       "  padding-left: 1em;\n",
       "}\n",
       "\n",
       "\n",
       "/* Toggleable style: style used for estimator/Pipeline/ColumnTransformer box that is\n",
       "clickable and can be expanded/collapsed.\n",
       "- Pipeline and ColumnTransformer use this feature and define the default style\n",
       "- Estimators will overwrite some part of the style using the `sk-estimator` class\n",
       "*/\n",
       "\n",
       "/* Pipeline and ColumnTransformer style (default) */\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable {\n",
       "  /* Default theme specific background. It is overwritten whether we have a\n",
       "  specific estimator or a Pipeline/ColumnTransformer */\n",
       "  background-color: var(--sklearn-color-background);\n",
       "}\n",
       "\n",
       "/* Toggleable label */\n",
       "#sk-container-id-1 label.sk-toggleable__label {\n",
       "  cursor: pointer;\n",
       "  display: flex;\n",
       "  width: 100%;\n",
       "  margin-bottom: 0;\n",
       "  padding: 0.5em;\n",
       "  box-sizing: border-box;\n",
       "  text-align: center;\n",
       "  align-items: start;\n",
       "  justify-content: space-between;\n",
       "  gap: 0.5em;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 label.sk-toggleable__label .caption {\n",
       "  font-size: 0.6rem;\n",
       "  font-weight: lighter;\n",
       "  color: var(--sklearn-color-text-muted);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 label.sk-toggleable__label-arrow:before {\n",
       "  /* Arrow on the left of the label */\n",
       "  content: \"▸\";\n",
       "  float: left;\n",
       "  margin-right: 0.25em;\n",
       "  color: var(--sklearn-color-icon);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 label.sk-toggleable__label-arrow:hover:before {\n",
       "  color: var(--sklearn-color-text);\n",
       "}\n",
       "\n",
       "/* Toggleable content - dropdown */\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content {\n",
       "  display: none;\n",
       "  text-align: left;\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content.fitted {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content pre {\n",
       "  margin: 0.2em;\n",
       "  border-radius: 0.25em;\n",
       "  color: var(--sklearn-color-text);\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content.fitted pre {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 input.sk-toggleable__control:checked~div.sk-toggleable__content {\n",
       "  /* Expand drop-down */\n",
       "  display: block;\n",
       "  width: 100%;\n",
       "  overflow: visible;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 input.sk-toggleable__control:checked~label.sk-toggleable__label-arrow:before {\n",
       "  content: \"▾\";\n",
       "}\n",
       "\n",
       "/* Pipeline/ColumnTransformer-specific style */\n",
       "\n",
       "#sk-container-id-1 div.sk-label input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-label.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Estimator-specific style */\n",
       "\n",
       "/* Colorize estimator box */\n",
       "#sk-container-id-1 div.sk-estimator input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-estimator.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-label label.sk-toggleable__label,\n",
       "#sk-container-id-1 div.sk-label label {\n",
       "  /* The background is the default theme color */\n",
       "  color: var(--sklearn-color-text-on-default-background);\n",
       "}\n",
       "\n",
       "/* On hover, darken the color of the background */\n",
       "#sk-container-id-1 div.sk-label:hover label.sk-toggleable__label {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "/* Label box, darken color on hover, fitted */\n",
       "#sk-container-id-1 div.sk-label.fitted:hover label.sk-toggleable__label.fitted {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Estimator label */\n",
       "\n",
       "#sk-container-id-1 div.sk-label label {\n",
       "  font-family: monospace;\n",
       "  font-weight: bold;\n",
       "  display: inline-block;\n",
       "  line-height: 1.2em;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-label-container {\n",
       "  text-align: center;\n",
       "}\n",
       "\n",
       "/* Estimator-specific */\n",
       "#sk-container-id-1 div.sk-estimator {\n",
       "  font-family: monospace;\n",
       "  border: 1px dotted var(--sklearn-color-border-box);\n",
       "  border-radius: 0.25em;\n",
       "  box-sizing: border-box;\n",
       "  margin-bottom: 0.5em;\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-estimator.fitted {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "/* on hover */\n",
       "#sk-container-id-1 div.sk-estimator:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-estimator.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Specification for estimator info (e.g. \"i\" and \"?\") */\n",
       "\n",
       "/* Common style for \"i\" and \"?\" */\n",
       "\n",
       ".sk-estimator-doc-link,\n",
       "a:link.sk-estimator-doc-link,\n",
       "a:visited.sk-estimator-doc-link {\n",
       "  float: right;\n",
       "  font-size: smaller;\n",
       "  line-height: 1em;\n",
       "  font-family: monospace;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  border-radius: 1em;\n",
       "  height: 1em;\n",
       "  width: 1em;\n",
       "  text-decoration: none !important;\n",
       "  margin-left: 0.5em;\n",
       "  text-align: center;\n",
       "  /* unfitted */\n",
       "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-unfitted-level-1);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link.fitted,\n",
       "a:link.sk-estimator-doc-link.fitted,\n",
       "a:visited.sk-estimator-doc-link.fitted {\n",
       "  /* fitted */\n",
       "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-fitted-level-1);\n",
       "}\n",
       "\n",
       "/* On hover */\n",
       "div.sk-estimator:hover .sk-estimator-doc-link:hover,\n",
       ".sk-estimator-doc-link:hover,\n",
       "div.sk-label-container:hover .sk-estimator-doc-link:hover,\n",
       ".sk-estimator-doc-link:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "div.sk-estimator.fitted:hover .sk-estimator-doc-link.fitted:hover,\n",
       ".sk-estimator-doc-link.fitted:hover,\n",
       "div.sk-label-container:hover .sk-estimator-doc-link.fitted:hover,\n",
       ".sk-estimator-doc-link.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "/* Span, style for the box shown on hovering the info icon */\n",
       ".sk-estimator-doc-link span {\n",
       "  display: none;\n",
       "  z-index: 9999;\n",
       "  position: relative;\n",
       "  font-weight: normal;\n",
       "  right: .2ex;\n",
       "  padding: .5ex;\n",
       "  margin: .5ex;\n",
       "  width: min-content;\n",
       "  min-width: 20ex;\n",
       "  max-width: 50ex;\n",
       "  color: var(--sklearn-color-text);\n",
       "  box-shadow: 2pt 2pt 4pt #999;\n",
       "  /* unfitted */\n",
       "  background: var(--sklearn-color-unfitted-level-0);\n",
       "  border: .5pt solid var(--sklearn-color-unfitted-level-3);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link.fitted span {\n",
       "  /* fitted */\n",
       "  background: var(--sklearn-color-fitted-level-0);\n",
       "  border: var(--sklearn-color-fitted-level-3);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link:hover span {\n",
       "  display: block;\n",
       "}\n",
       "\n",
       "/* \"?\"-specific style due to the `<a>` HTML tag */\n",
       "\n",
       "#sk-container-id-1 a.estimator_doc_link {\n",
       "  float: right;\n",
       "  font-size: 1rem;\n",
       "  line-height: 1em;\n",
       "  font-family: monospace;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  border-radius: 1rem;\n",
       "  height: 1rem;\n",
       "  width: 1rem;\n",
       "  text-decoration: none;\n",
       "  /* unfitted */\n",
       "  color: var(--sklearn-color-unfitted-level-1);\n",
       "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 a.estimator_doc_link.fitted {\n",
       "  /* fitted */\n",
       "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-fitted-level-1);\n",
       "}\n",
       "\n",
       "/* On hover */\n",
       "#sk-container-id-1 a.estimator_doc_link:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 a.estimator_doc_link.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-3);\n",
       "}\n",
       "\n",
       ".estimator-table summary {\n",
       "    padding: .5rem;\n",
       "    font-family: monospace;\n",
       "    cursor: pointer;\n",
       "}\n",
       "\n",
       ".estimator-table details[open] {\n",
       "    padding-left: 0.1rem;\n",
       "    padding-right: 0.1rem;\n",
       "    padding-bottom: 0.3rem;\n",
       "}\n",
       "\n",
       ".estimator-table .parameters-table {\n",
       "    margin-left: auto !important;\n",
       "    margin-right: auto !important;\n",
       "}\n",
       "\n",
       ".estimator-table .parameters-table tr:nth-child(odd) {\n",
       "    background-color: #fff;\n",
       "}\n",
       "\n",
       ".estimator-table .parameters-table tr:nth-child(even) {\n",
       "    background-color: #f6f6f6;\n",
       "}\n",
       "\n",
       ".estimator-table .parameters-table tr:hover {\n",
       "    background-color: #e0e0e0;\n",
       "}\n",
       "\n",
       ".estimator-table table td {\n",
       "    border: 1px solid rgba(106, 105, 104, 0.232);\n",
       "}\n",
       "\n",
       ".user-set td {\n",
       "    color:rgb(255, 94, 0);\n",
       "    text-align: left;\n",
       "}\n",
       "\n",
       ".user-set td.value pre {\n",
       "    color:rgb(255, 94, 0) !important;\n",
       "    background-color: transparent !important;\n",
       "}\n",
       "\n",
       ".default td {\n",
       "    color: black;\n",
       "    text-align: left;\n",
       "}\n",
       "\n",
       ".user-set td i,\n",
       ".default td i {\n",
       "    color: black;\n",
       "}\n",
       "\n",
       ".copy-paste-icon {\n",
       "    background-image: url();\n",
       "    background-repeat: no-repeat;\n",
       "    background-size: 14px 14px;\n",
       "    background-position: 0;\n",
       "    display: inline-block;\n",
       "    width: 14px;\n",
       "    height: 14px;\n",
       "    cursor: pointer;\n",
       "}\n",
       "</style><body><div id=\"sk-container-id-1\" class=\"sk-top-container\"><div class=\"sk-text-repr-fallback\"><pre>KNeighborsClassifier(n_neighbors=1)</pre><b>In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. <br />On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.</b></div><div class=\"sk-container\" hidden><div class=\"sk-item\"><div class=\"sk-estimator fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-1\" type=\"checkbox\" checked><label for=\"sk-estimator-id-1\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow\"><div><div>KNeighborsClassifier</div></div><div><a class=\"sk-estimator-doc-link fitted\" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.7/modules/generated/sklearn.neighbors.KNeighborsClassifier.html\">?<span>Documentation for KNeighborsClassifier</span></a><span class=\"sk-estimator-doc-link fitted\">i<span>Fitted</span></span></div></label><div class=\"sk-toggleable__content fitted\" data-param-prefix=\"\">\n",
       "        <div class=\"estimator-table\">\n",
       "            <details>\n",
       "                <summary>Parameters</summary>\n",
       "                <table class=\"parameters-table\">\n",
       "                  <tbody>\n",
       "                    \n",
       "        <tr class=\"user-set\">\n",
       "            <td><i class=\"copy-paste-icon\"\n",
       "                 onclick=\"copyToClipboard('n_neighbors',\n",
       "                          this.parentElement.nextElementSibling)\"\n",
       "            ></i></td>\n",
       "            <td class=\"param\">n_neighbors&nbsp;</td>\n",
       "            <td class=\"value\">1</td>\n",
       "        </tr>\n",
       "    \n",
       "\n",
       "        <tr class=\"default\">\n",
       "            <td><i class=\"copy-paste-icon\"\n",
       "                 onclick=\"copyToClipboard('weights',\n",
       "                          this.parentElement.nextElementSibling)\"\n",
       "            ></i></td>\n",
       "            <td class=\"param\">weights&nbsp;</td>\n",
       "            <td class=\"value\">&#x27;uniform&#x27;</td>\n",
       "        </tr>\n",
       "    \n",
       "\n",
       "        <tr class=\"default\">\n",
       "            <td><i class=\"copy-paste-icon\"\n",
       "                 onclick=\"copyToClipboard('algorithm',\n",
       "                          this.parentElement.nextElementSibling)\"\n",
       "            ></i></td>\n",
       "            <td class=\"param\">algorithm&nbsp;</td>\n",
       "            <td class=\"value\">&#x27;auto&#x27;</td>\n",
       "        </tr>\n",
       "    \n",
       "\n",
       "        <tr class=\"default\">\n",
       "            <td><i class=\"copy-paste-icon\"\n",
       "                 onclick=\"copyToClipboard('leaf_size',\n",
       "                          this.parentElement.nextElementSibling)\"\n",
       "            ></i></td>\n",
       "            <td class=\"param\">leaf_size&nbsp;</td>\n",
       "            <td class=\"value\">30</td>\n",
       "        </tr>\n",
       "    \n",
       "\n",
       "        <tr class=\"default\">\n",
       "            <td><i class=\"copy-paste-icon\"\n",
       "                 onclick=\"copyToClipboard('p',\n",
       "                          this.parentElement.nextElementSibling)\"\n",
       "            ></i></td>\n",
       "            <td class=\"param\">p&nbsp;</td>\n",
       "            <td class=\"value\">2</td>\n",
       "        </tr>\n",
       "    \n",
       "\n",
       "        <tr class=\"default\">\n",
       "            <td><i class=\"copy-paste-icon\"\n",
       "                 onclick=\"copyToClipboard('metric',\n",
       "                          this.parentElement.nextElementSibling)\"\n",
       "            ></i></td>\n",
       "            <td class=\"param\">metric&nbsp;</td>\n",
       "            <td class=\"value\">&#x27;minkowski&#x27;</td>\n",
       "        </tr>\n",
       "    \n",
       "\n",
       "        <tr class=\"default\">\n",
       "            <td><i class=\"copy-paste-icon\"\n",
       "                 onclick=\"copyToClipboard('metric_params',\n",
       "                          this.parentElement.nextElementSibling)\"\n",
       "            ></i></td>\n",
       "            <td class=\"param\">metric_params&nbsp;</td>\n",
       "            <td class=\"value\">None</td>\n",
       "        </tr>\n",
       "    \n",
       "\n",
       "        <tr class=\"default\">\n",
       "            <td><i class=\"copy-paste-icon\"\n",
       "                 onclick=\"copyToClipboard('n_jobs',\n",
       "                          this.parentElement.nextElementSibling)\"\n",
       "            ></i></td>\n",
       "            <td class=\"param\">n_jobs&nbsp;</td>\n",
       "            <td class=\"value\">None</td>\n",
       "        </tr>\n",
       "    \n",
       "                  </tbody>\n",
       "                </table>\n",
       "            </details>\n",
       "        </div>\n",
       "    </div></div></div></div></div><script>function copyToClipboard(text, element) {\n",
       "    // Get the parameter prefix from the closest toggleable content\n",
       "    const toggleableContent = element.closest('.sk-toggleable__content');\n",
       "    const paramPrefix = toggleableContent ? toggleableContent.dataset.paramPrefix : '';\n",
       "    const fullParamName = paramPrefix ? `${paramPrefix}${text}` : text;\n",
       "\n",
       "    const originalStyle = element.style;\n",
       "    const computedStyle = window.getComputedStyle(element);\n",
       "    const originalWidth = computedStyle.width;\n",
       "    const originalHTML = element.innerHTML.replace('Copied!', '');\n",
       "\n",
       "    navigator.clipboard.writeText(fullParamName)\n",
       "        .then(() => {\n",
       "            element.style.width = originalWidth;\n",
       "            element.style.color = 'green';\n",
       "            element.innerHTML = \"Copied!\";\n",
       "\n",
       "            setTimeout(() => {\n",
       "                element.innerHTML = originalHTML;\n",
       "                element.style = originalStyle;\n",
       "            }, 2000);\n",
       "        })\n",
       "        .catch(err => {\n",
       "            console.error('Failed to copy:', err);\n",
       "            element.style.color = 'red';\n",
       "            element.innerHTML = \"Failed!\";\n",
       "            setTimeout(() => {\n",
       "                element.innerHTML = originalHTML;\n",
       "                element.style = originalStyle;\n",
       "            }, 2000);\n",
       "        });\n",
       "    return false;\n",
       "}\n",
       "\n",
       "document.querySelectorAll('.fa-regular.fa-copy').forEach(function(element) {\n",
       "    const toggleableContent = element.closest('.sk-toggleable__content');\n",
       "    const paramPrefix = toggleableContent ? toggleableContent.dataset.paramPrefix : '';\n",
       "    const paramName = element.parentElement.nextElementSibling.textContent.trim();\n",
       "    const fullParamName = paramPrefix ? `${paramPrefix}${paramName}` : paramName;\n",
       "\n",
       "    element.setAttribute('title', fullParamName);\n",
       "});\n",
       "</script></body>"
      ],
      "text/plain": [
       "KNeighborsClassifier(n_neighbors=1)"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "knn.fit(X, y) # TODO"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 4. 用模型推理(预测)用户的喜好"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "new_user = np.array([[3, 4]])\n",
    "prediction = knn.predict(new_user)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 5. 数据可视化"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAkUAAAHJCAYAAACL5E3/AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjEsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvc2/+5QAAAAlwSFlzAAAPYQAAD2EBqD+naQAAVv5JREFUeJzt3Qd4U1UbwPG3QCmrZcqQjcjeIFMFZIMKosjHVoafCgqyBBwsWSICKiIKAg5EhiAqQ4aALGUqS2TJHrJHBQrke94Tky9t05E2adb/9zyXNjc3N+fkJuTtOe85J8RisVgEAAAgyKXydgEAAAB8AUERAAAAQREAAIAVQREAAABBEQAAgBVBEQAAAEERAACAFUERAAAAQREAAIAVQRGC3l9//SUhISFmi88zzzxjjhkyZIj4C1uZdfvvf/8b53E3b96UbNmy2Y9dvXq1eKusnnxuPbetjrYtTZo0kjt3bmnevLn89NNPkhLu3Lkjb775ptx3332SNm1aUw6tPwDvSuPl5weQQubOnSvvv/+++RKO6YcffpCLFy9KsMiVK5c0btzY/H7jxg3ZsWOHLFq0SL777juZNGmSvPDCCx59/okTJ8rw4cPl3nvvlZYtW0q6dOnkwQcf9OhzAkgYQREQBCpWrCjbt2+XxYsXS4sWLWLd/8UXX0jq1KmlTJky8ttvv3mljKNGjZIBAwZIgQIFPP5cJUqUkBkzZthv6xKQw4YNM62Affr0kSeffFJy5szpsedfuHCh+fnzzz9LkSJFPPY8AFxD9xkQBNq0aSOpUqWSL7/8MtZ9ly5dMsFSvXr1TDeSt+TJk8cEKxkyZEjx59buqzfeeMN0Z/3zzz/y448/evT5jh8/bn4SEAG+haAISKZr166ZVo7y5ctL5syZJVOmTObLtVWrVrJs2bJYx0dGRprjtfVGj9WtevXqMnPmzDi/sAsVKiS3bt0yrRkaOISFhTlt8Ykv4HjkkUfk+++/l8uXL0e7b86cOSanqH379vGe49ixYyYvqWDBgub5tSVFu342b94c7bht27aZMlerVi3Oc2k3nh7Tu3fvROUUufqaJYUGjXoNbXV1zEHSsp0+fVq6du0q+fLlM3lIEyZMsD9Wj+/Ro4e57toVpvlZjz76qGzYsCHac9jqePjwYXPbMbdJc9s8+R5J6jk1/2nMmDFSrFgxc878+fPLq6++at4zzly/ft0cX6VKFYmIiJCMGTOa8nTv3l3+/PPPWMf/8ssv5rOi71Ht2tXXV1/no0ePxnu9AE+g+wxIBv3CqF+/vvmPPUeOHFKnTh3zpagtAdr6ol8IjRo1sh9/9uxZadCggfz++++mVaZ27dqm60a/PPULc8uWLSZgiOnu3bvmC27t2rXmMeXKlZPs2bO7VNZ27drJihUrZP78+dK5c2f7fm090taZJ554wmlLktq5c6cJqs6dOyfFixc3wZB+aS1YsMDk4cyaNct8salKlSqZL8Fff/1VDh48aAKFmGzPk1AglpzXLCmuXr1qfuqXv6O///5bHnjgAbl9+7bJ/dE8JFuL1saNG6VZs2YmJ0tfG/1dj9eAeOnSpaaurVu3Nsfa8obmzZtngodOnTrZn0ODFE+9R5LzGrZt29a8l/W9rfXTLr+3335bTpw4YbpdHZ06dco8z+7duyVr1qzmMfpaHjp0SD766CO5//77TXBl8+GHH8pLL71kftfX96GHHpJ9+/bJtGnTTI7XmjVrpGTJkkm6lkCSWIAgd/jwYYt+FBL6OHTq1MkcM3jwYPu+VatWmX0PPPCA5Z9//ol2/OXLly1btmyJtq9p06bm+J49e1pu3Lhh33/69GlLlSpVzH1LliyJ9hhb2YoWLWo5fvy4S3Wzlfnzzz+3XLlyxZI+fXpL3bp17fcfOXLEEhISYmnTpo253ahRI3P8Tz/9ZD/m7t27lrJly5r9/fv3N7dt5s2bZ0mVKpUlU6ZMlpMnT9r3Dx8+3Bw/bNiwWGU6cOCAua9EiRJOy+r43El9zeKi59bja9euHeu+M2fOWCIiIsz9y5cvj3a8bk888YTTa5wnTx5L6tSpLV988UW0+zZv3mzJmjWreW3Onj0b7b6CBQvG+X7zxHskOecsWbKk5dSpU/b9hw4dsmTJksXcp9fSUb169cz+p59+2nL16tVYn7PffvvNfnvjxo3mdcubN2+sz8nUqVPNeapVq+b0NQI8haAIQS85QdHXX39t9vXq1SvB59m+fbs9gLpz506s+7dt22buf/zxx6Ptt5Vt7ty5LtUrZlCk9MtKgxjbF+fIkSPN/T/88EOcQZEt8CtQoIDl1q1bsZ6jZcuW5v633nor2hens8BHDR061NyngZOzsjo+d1JfM1eCIg10Nm3aZL6A9b7ixYtbbt++He34sLAwp8HG+PHjzf19+vRx+nzvvvuuuV9/JiYo8sR7JLnntAWIjnr06GHumz59un3fL7/8YvblzJnTBOAJad68uTn+u+++c3q/lkfv1/IBKYWcIiAZKlSoYHJRpk+fLp988omcP38+zmNtybvaxaGPicmW66HdTs7yOx577LFkl1e7q7SbRbu7lHbtaG5Qw4YN43yMdpeop59+WkJDQ2Pd36FDh2jHqcKFC0vNmjXljz/+MDlGzrrOtDsvIcl5zeKj3TK2XJ706dOb3BrtAi1atKgZGaYj8Rxpl2DevHnjLJ92Jzqj3UEqseXzxHskOefU6123bt1Y+21dYNpdZqNds7ak/vDw8Hjrqe/BlStXmi5Ix+7l5Lx2gDsQFCHoJTRpo431j+fox+uXg+ZXaBLrc889ZwIMTdbVBGLN33BkS6R97bXXYk0gaNs0aVvzdmLS88bMc0kKnZtH80w0MNEh+pr7ofkumjgcl5MnT5qfmnTrjG2/5pg4sgU9jnlKmruiybYaMGnglJDkvGYJzVOk+Ty6denSRfr37y/ffPON7Nmzx+RDxRTXNAG28tWqVctp2TRPRiW2fJ54jyTnnJp/FDNAVLagxzHZ2pac7iyHLCZ9Ln1O/dzYJq+MufXr189+LJBSSLRG0HMcAq7/Scc1JFzvU5o87UjntdFWFG1hWL58uWkxGT9+vBmdpD979uxp/+vYlmybmC8OR5q87Q76l7+WdfLkyTJo0KBEJzsnJajUYKtXr14ye/ZsGTt2bLQpARLTSpTc18yVeYqS+vrbyvfUU0/Fel/EfL7E8MR7JDnndNay5A62MmkLlc4JFZ/SpUt7pAyAMwRFCHo6fFq7UHR+Gh0loxMYOqP3KR0yHJMOU9ZRNLrpCCUNBJ599lnTAtGxY0czEsf2OO3G0EDKWzQI0qBIR0ZpS1fVqlXjPV5nXVZHjhxxer+tJSJm95K2SGnXiE4DoEPbdcSTvi4amNlGYyXEV16z+Mqno6V00snKlSu75Xzurm9KvYb6GVA64jAhOlJTgzhb13NiW2sBT6P7DEFPuwe0+8O23IUz2jWgS0Hof+K2Y+OiXVEaeGjXic4bs3//frNfhyorHcbuTdp1pV18GrQ4Ds2Piy23Q5cJ0SkIYrINy7Yd58jWIqQ5TKtWrTJz/WiglNjpBHzlNUup8nmivin1GurUFOqrr74yXWMJfUZ0uP6VK1dMbhHgKwiKABF7F9fo0aNNwq0jnexQgwdt8teEWttfxEoXENUEU1t3gI1Ozrd3717zF7DtL3WdzFC/oNavX28mstMvhJh0iQ1twfE0DfA0V0Mn4UuIfnmVLVvWtAjpIqa23CrbF63m4mg3iLMASxdZ1fwTnRvp008/danrzNdeM2d0MkvN5dG8so8//jjW+0BbDXW+ol27dnmtvin1GmqLoyZl65xIml+n8zA50vePzndlozlO+keGtqg6m7BTAyt9z2gLLpBiUmycG+DjdA4e/UjokPUaNWpY2rZta2nWrJklc+bMZn+ZMmVizTdjG5J9zz33WBo3bmxp166dpWHDhmYIt+5/6aWXYs2FU7FiRXOfzvVSp04d+/Pkz5/fPpeMI92nQ7iTIuaQ/IQ4G5Kvfv/9d0v27Nnt89bovEa1atUyt9OkSWOmJohLx44d7cO7w8PDLZGRkfGWNeZzJ+U1S8o8RfEdr2WLi863kyNHDnOclqdJkyamfI888oh9Pp8FCxYkep4iT7xH3H1OHYofc3oKpdMW6JQGel+2bNnMsPpWrVpZKlWqZD5X+nlxNHnyZDNXke3zpdM7tG7d2kyPYPsMXbx4Mc56Ae5GUAQ40AnsdP6U3Llzmy97ncyvatWqljFjxliuXbsW6/j9+/dbXn/9dRMg6CR+adOmNZPR6SR28+fPjzbRoeO8OO+9956lZs2aJuDSx+gXk35Rjx071nLs2DGfC4psEz1269bNlDU0NNQEAi1atDDz08Rn2bJl9qBIA6SEyursuV19zVIyKFI6uaEG1aVLl7ZkyJDBbPfdd595L82YMSPWRIbxBUWeeo+485xxBUVK5yjSSTvLlStnJgvVySt1viqd20g/L87mUdLXV59Ly6RBm76OnTt3tnz//fdOP0OAp4ToPynXLgUAAOCbyCkCAAAgKAIAALAiKAIAACAoAgAAsCIoAgAAICgC4qYzUevkcuvWrUvR59VJ63TG388++yxFnxcAgl3QDcnXGWd11W+dZZf1dhCfhx9+2MzKu3XrVnNbZ25+44035NSpU2a5C13du2HDhtFWgU+Mpk2bmtmFHel6YI6rgevq7TrLb1zrjQFAsLFYLHL16lWzHqOnFisOuqDo+PHj0ZZpAAAA/kPXonS2MLc7pJEgoy1Ethc1IiLCreeOioqSH3/80bQe6F/+gSbQ6+dYR+0y05XkL126FO/x1atXNy2PR48edamlSFufzpw5E+9xBQoUkFKlSrl1Xa9Av4aBXr9gqCP1839RHqqjrtunjRq273FPCLqgyNZlpgGRJ4KiDBkymPMG4ps90OvnWEcNinQl94TeI7pYpS6G6sp7SfOFbty4IVmzZjVNwHnz5jUri9eoUSPacSVKlJA9e/a49X0a6Ncw0OsXDHWkfv4vysN19GTqC4nWgBPaiqNBS3ymTJliVv7Wlcdd8cgjj8grr7wiX3/9tQwZMkTOnz8vDz30kGlxcqTBUsyVxgEAnhN0LUVAYty+fVvCwsLivH/BggXywgsvSL169WTgwIEunfvNN9+Mdrt9+/ZSqFAhGTRokMyYMcO+P2PGjCahGwCQMmgpApzQLjEd5eDMokWL5MknnzTdXStWrEj2cxUsWNA0Ne/bty/a/r///tuMcAMApAyCIsCJ0qVLO02E/vbbb6VFixZSpUqVWMPqk+r06dMmN0mHmTrSICnmPgCA5xAUAU507NjRJEMfPnw4WpfZE088Ybq6pk6dKr///rvZ9u7d69K5NaCaOHGiSebWvCQNwNRbb70V7Tgd0aajNwAAKYOgCHCiZcuWJqfn9ddft+/TQEan9dJAqXz58vatbNmy9mM00NGRERMmTIjz3GfPnpXevXub5OoXX3zRPI92w5UsWdJ+zMcff2wmGh05cqQHawkAcERQBMShb9++Mm/ePJN0rXSGaQ2KYm62+9Wvv/5qgqLmzZvHeV5tAdIEan2s/tTbOiLNkbYa1a9fX7Jly+bBGgIAHBEUAXHQ4fK2iRYTS+cbatKkiRQuXDhZa58VK1ZMZs+eneRzAABcx5B8IB6aR+SKzZs3u2XkmztGtQEAXENLEYKe5vhoDpEOi7d1V+kIM8ckawBA4Evl7e4Jzb9w3HRpg/jMnTvXHJMuXTqT4Lp48eIUKy8CjwY+2tWlQ+11nTHt+lJr164177NffvnF20UEAARLS5EORz516pR909E7cdmwYYO0adNGunTpItu3bzd/zeu2a9euFC0zAocmOGvC82uvvSbPPfecNGjQwOzX29pypDlFAIDg4PWgSBfHzJ07t33LkSNHnMfqkOjGjRtLv379zPDl4cOHS6VKleSDDz5I0TIjMGgwfeTIEXn00UclX7580e7TxWCfeuopuXDhgmlFAgAEPq8nWu/fv9/M2qvdYbpswqhRo6RAgQJOj924caOZ38VRo0aNZOHChXGe/+bNm2azuXLlin0VX93cyXY+d5/XVwRa/fR9o++7ypUr21dd1n+LLFokl8uXl4oVK0r69Onlu+++C5gWo0C7hsFWv2CoI/Xzf1EeqmNKvGYhFu078JIlS5aY4cfFixc3XWdDhw6VEydOmL/gw8PDYx2v60DNnDnTdKHZfPjhh+ZxzpZksOUt6f0xzZo1y3SPAI4KL14s5T7+WK7nyiVb+vaVS/ff7+0iAQBEJDIyUtq2bSuXL1+WiIiIwGspsiW1qnLlykm1atXM4phz5swxeUPuoCuYO7YuaUtR/vz5zfIJ7n5RNYpdvny5yUsJDQ2VQBNo9Tt58qTJadNJEps1a2bdmSOH3Jczp2Q8c0ZqvfqqDEidWp5YtUqqVqsmgSDQrmGw1S8Y6kj9/F+Uh+po6+kJ6O4zR1myZDGT1h04cMDp/ZpzFLNFSG/r/riEhYWZLSa9UJ56Q3ry3L4gUOqnAbi2Umr3mHbh6pIdZwsVktXvvisFhg2Tsn/+Ke/cvSsybpzIp5+KZM0qgSJQrmGw1i8Y6kj9/F+om+uYEq+X1xOtHWlX2sGDByVPnjxO79eco5UrV0bbp9Go7geSYs2aNZIzZ06ZPHmy6Wb98ssv5XamTFLt6FHpmy6dWNKm1eQjkYoVdQ0PbxcXABCoQZGuLaVfSn/99ZcZbq8rkKdOndqeM6QrlWv3l03Pnj1l6dKlMm7cOPnjjz9MvtCWLVukR48eXqwF/JnOHn3s2DGz1pi+92wr3vd65RV56+JFCdm4UeS++0SOHxe5dcvbxQUABGpQdPz4cRMAaRfG008/bYZBb9q0Se655x5zvy6UqQnYNjVr1jQJ0rqCuHZ16GKdOoKoTJkyXqwF/J1OC6HzEmlwpO9Jpa1GOjJNKlUS2bZNZP58kQcf/P+D7tzxXoEBAIGXU5TQgpe6KnlMrVq1MhuQYjQh33HV+z17RJ54QmTqVJGHHvJmyQAAgZpTBPiFN94Q+fNPkbp1RUaOFNFkbACA3yMoAlw1c6ZI+/bWLrTXXtO5JXRVWW+XCgCQTARFgKsyZRL57DORadNE0qcX+fFHkQoVtL/X2yUDACQDQRGQFLosSOfOIps3i5QqJaIDAurVE1m1ytslAwAEwuSNgN8pXdo6f9FLL4nopKMPP+ztEgEAkoigCEiujBmtM15HRur4fus+XYRYW5Ech/EDAHwa3WeAuzguMPzqq9ZWo9dfF7l925ulAgAkEkER4G46RF9biiwWkREjrLlGJ054u1QAgAQQFAHuliqVyOTJIl99JRIeLrJ2rXV02tKl3i4ZACAeBEWAp/znPyJbt1oXkz13zjqf0YABIlFR3i4ZAMAJgiLAk+6/X2TDBpHu3a23tQXp5ElvlwoA4ASjzwBP04VlP/jAuiyIdq0VLOjtEgEAnCAoAlLKk09Gv60zYWue0ejRImnTeqtUAIB/ERQB3nDtmkiHDtY109avF5k9W6RwYW+XCgCCGjlFgLfWT/v4Y5EsWawzYmsy9oIF3i4VAAQ1giLAW5o3F9mxQ6R6dZHLl0VathR5+WXrHEcAgBRHUAR4kyZd6zxG/fpZb7//vkitWiJXr3q7ZAAQdAiKAG8LDRV5+22R778XyZ5dpEwZ66SPAIAURaI14CuaNbN2p2XN+v99Fy9ah/SnT+/NkgFAUKClCPAl+fKJZMz4/zXU2rWz5hzt2+ftkgFAwCMoAnzVkSMiW7aI/P67SOXKIl9+6e0SAUBAIygCfJXOW/TbbyJ16ohcvy7Svr1I164ikZHeLhkABCSCIsCX5ckjsmKFyODBIiEhItOmiVStKrJnj7dLBgABh6AI8HWpU4sMGWINjnLnFtm9W6R1a2vOEQDAbQiKAH/xyCPW0Wk6Su3TT62LywIA3IYh+YA/yZXLOp+RI103rXRpkbJlvVUqAAgI/KkJ+DNtOerUyZpn9MknIhaLt0sEAH6LoAjwZ3nzWrvVbtwQee45kbZtRa5c8XapAMAvERQB/uyee0R++EFkzBhrQrZ2pemcRtu3e7tkAOB3CIoAf6cJ1/37WxeWzZ9f5MAB6yzYH33k7ZIBgF8hKAICRc2a1haixx4TuXVL5MIFb5cIAPyKzwRFo0ePlpCQEOnVq1ecx8yYMcMc47il08UyAVhlzy7y7bciX38tMmDA//ffvu3NUgGAX/CJIfmbN2+WKVOmSLly5RI8NiIiQvY5LI6pgREAB/qZePrp/9/+5x+Rhx4S6dBB5IUXvFkyAPBpXm8punbtmrRr104++eQTyZo1a4LHaxCUO3du+5ZL520BELeZM0W2bhXp1UtSP/WUhF696u0SAYBP8npLUffu3aVZs2ZSv359eeuttxIVRBUsWFDu3r0rlSpVkpEjR0ppnbguDjdv3jSbzZV/hytHRUWZzZ1s53P3eX1FoNcvYOvYubOkunlTUvXvL6m++07qbNokd/LlE6lVSwJNQF6/IKsj9fN/UR6qY0q8ZiEWi/dme5s9e7aMGDHCdJ9pblCdOnWkQoUKMmHCBKfHb9y4Ufbv32+62S5fvizvvPOOrF27Vnbv3i359D95J4YMGSJDhw6NtX/WrFmSIUMGt9cJ8FWZDxyQKu+8I5lOn5a7qVPLng4d5ODjj7NcCAC/EBkZKW3btjXf/5pKE1BB0bFjx6RKlSqyfPlyey5RQkGRs6ixZMmS0qZNGxk+fHiiW4ry588v586dc/uLquXR+jRo0EBCQ0Ml0AR6/YKhjlHnzsnFVq0k7/r15vadAQPk7rBhEigC/foFQx2pn/+L8lAd9fs7R44cHg2KvNZ9tnXrVjl79qzpArO5c+eOafn54IMPTCCTWieji4e+2BUrVpQDOi9LHMLCwszm7LGeekN68ty+INDrF9B1zJFDtvTtK7nbtJHUo0ZJ6u7dJXUA1jNgr18Q1ZH6+b9QN9cxJV4vr7Wb16tXT3bu3Ck7duywb9pypEnX+ntCAZEtiNJz5MmTJ0XKDASEkBC5q0uC6B8Tjt3OK1aI3L3rzZIBgFd5raUoPDxcypQpE21fxowZJXv27Pb9HTt2lLx588qoUaPM7WHDhkn16tWlaNGicunSJRk7dqwcOXJEunbt6pU6AH7NcY6vRYtEmjcXadhQ5PPPRXLm9GbJAMArfDrD8ujRo3Lq1Cn77YsXL0q3bt1MHlHTpk1N/+KGDRukVKlSXi0n4Pd0mH769CI//ihSoYLI6tXeLhEABN+QfEerY/xHHPP2+PHjzQbAzdq1swZDrVqJ7N2r/dsigweLvPaadaFZAAgCPt1SBCAF6XxfmzeLPPusNbdIgyLtTjt92tslA4AUQVAE4P8yZhT59FORzz4T0Xm8Vq0S+Xf4PgAEOp/qPgPgI3SdtAcesC4u++ST3i4NAKQIWooAOFeihMirr/7/tg56eOopkRMnvFkqAPAYgiIAifP88yLz51sTspcu9XZpAMDtCIoAJM7YsdaA6Nw5kSZNRAYM0Pn8vV0qAHAbgiIAiVOsmK7KLPLii9bbY8bogoW6kKG3SwYAbkFQBMC1WbAnTRKZM0dEF2TcsMHaerRli7dLBgDJRlAEwHU6yeO2bSKVK1uXBNGkbADwcwzJB5A0991nncPozBmRTJms+3TSR53s8d57vV06AHAZLUUAki4sTKRAgf/f1mV4dGbsBQu8WSoASBKCIgDuoa1EOtnjpUsiLVuKvPyyyM2b3i4VACQaQREA90iVSmTlSpG+fa23339fpFYtkYMHvV0yAEgUgiIA7hMaap3P6PvvRbJnF9m6VaRiRetoNQDwcQRFANyvWTORHTusLUVXr4q0by9y5Ii3SwUA8WL0GQDPyJdPZPVqkTfftA7bL1jQ2yUCgHjRUuQh58+fl5w5c8pff/2V4s9dvXp1ma9rVAHeliaNyMiRIr16/X/fb7+JzJoVsJ/BW7duSaFChWQLE1oCfoegyENGjBghzZs3N/852rz88stSuXJlCQsLkwo6C3AyzZ49W0JCQqRFixbR9r/++usyYMAAuaujgQBfcu2ayNNPi7RrJ9K1q0hkZIp9BjVIaty4sdx7773mM5g/f37p0aOHXLlyJcnPMXr0aPMZ7OUQ9KVNm1b69u0rr776qlvqASDlEBR5QGRkpEybNk26dOkS677OnTtL69atk/0c+tev/sf70EMPxbqvSZMmcvXqVVmyZEmynwdw+zIh//mPSEiIyLRpItWqiezdmyKfwVSpUpkgadGiRfLnn3/KjBkzZMWKFfL8888n6Tk2b94sU6ZMkXLlysW6r127drJu3TrZvXt3suoBIGURFHmABiP6l6h2Yzl67733pHv37lKkSJFknf/OnTvmP92hQ4c6PVfq1KmladOmpiUJ8LnutKFDRZYvF8mVS2TXLpEqVURmzvT4ZzBr1qzywgsvSJUqVaRgwYJSr149efHFF+Xnn392+fzXrl0zn8FPPvnEnDcm3VerVi0+g4CfISjygPXr15tuMk8ZNmyYyZVw1hJlU7Vq1ST9Zw+kiHr1rKPT9Kd2oT3zjEinTiI3bqTYZ/DkyZPyzTffSO3atV0+v/5x06xZM6lfv36cx/AZBPwPQZEHHDlyxOQteII2yWu3gP6FGh99/mPHjpFXBN+VO7fIsmUiw4dbJ348edI6z5GHP4Nt2rSRDBkySN68eSUiIkKmTp3q0rm19Wfbtm0yatSoeI/T59dyAPAfBEUecOPGDUmnuRNupnlCHTp0MAFRjhw54j02ffr0JiC6yTIL8GWpU+vIAJGffhL54gvrbXX7tojF4pHP4Pjx401Q8+2338rBgweld+/eiT6v/qHRs2dP+fLLLxP8jOtnUHObAPgP5inygOzZs8vFixfdfl79D1wTrB977DH7PltLUJo0aWTfvn1yn65cLiIXLlyQjBkzmv+YAZ/38MPRb3fvbp30ccoUkfBwt34Gc+fObbYSJUpItmzZzGCFN954Q/LkyZPgebdu3Spnz56VSpUqRcvxW7t2rXzwwQfmjxDN6bN9Bu+55x6Xyw7AewiKPECH23/11VduP6/+J75z585Yw++1BWnixIlmiLHNrl27pKIurwD4m337rCPT7twR0bl+dIkQF6ewSOxn0PZHRWJbVDU5O+Zn8NlnnzWfTR2CbwuIFJ9BwP8QFHlAgwYNTLCif6k6jkw5cOCAGbVy+vRp+eeff2SHJpqKSKlSpczcJgnR5voyZcpE25clSxbzM+Z+TfBs2LChm2oEpKDixUXWrLEO3d+/X2cj1T4vER06r0P5k/gZXLx4sZw5c0YeeOAByZQpkxku369fPzNKzHE+sfiEh4fH+qxpi6y2TDn7DA7XfCkAfoOcIg8oW7asaV6fE2MRzK5du5q/HHVuE50nRX/XTUfB2OhEcDp/SnKcOHFCNmzYYP6CBfySrpmmfzQ8+qg244i8+KKIzu91+XKSP4Palaz5eA8++KCULFlSXnnlFXn88cfle1289l/aPa2fwdW6PEkybNy4US5fvixPPfVUss4DIGXRUuQhb775pvkrtFu3bmbSOJXQf7SHDx82uUH6l2tiOQugdD6kZ555RvLp2lOAv8qeXWTRImsrkc4OPXeuRi0iv/ySqBajmJ/BunXrmj8WEvoMautr+fLlE11MZ5/rCRMmmOcmpw/wLwRFHqJzmOzfv9+02jjm+sRHm/efe+45uf/++5P13DqHkSsjagCfpcGPvpf1DwXtTtPFZRPZhZbUz+CgQYOcTsjoytpn2lKlLVEA/AtBkQc5roeU2Anh3KFPnz5uOQ/gM3Q5kD/+EAkL+/++TZus+UeOAcyuXVJVF6AtUECkYkWXP4Njx45NdlE1P1DzmQD4H3KKkslisciCBQvMzLa2ZE39C/EP/Q8cgPs4BkRHj4o0bWoCH9Od9q9UixdLnl9/lVSs+wfAn4MiZ6tNOzN37lwz/FVHYmkTtTZ3ezMg+u9//ystW7aUo0ePSs2aNc1+XTpAhwQvXbrUa2UDApomXGsLkc4Y/eCDIuPGmckeQ3QSSO11+/cnAPhdUBTfatOONElSp+jXNb+2b98uLVq0MJvOB+INn3/+uRnN0rFjR7NivW0IvDada+CmI088MYkjEPTKlhXZts06Ok1nv+7bV6R2bQlZt87cbX5u3Wo9RrcTJ7xdYgB+wOtBUUKrTTvSCQobN25sRnXokFqdA0SH3epMst6g5dG5SWKOFgsNDTV10gnhZrp59W8A/8qc2dpSZKOLr9omYdSfVaqI6KKwumlXGwD4eqK142rTb731VoJzf8QcVdWoUSNZuHBhnI/RwMRxttorV66Yn1FRUWZLqtu3b8vevXuldevWpttPOf7UYb06KaO2giXneXyJrR6BUp9grGOg1S9Vy5aS2mGGadu4tJAY66bdadlS7gZInQPtGsZE/fxflIfqmBKvWYhFE2O8RFebHjFihAkcNEeoTp06JhdH5/iIa1SHtrxoF5rNhx9+KEOHDjUz1TozZMgQc39Ms2bNMitlA/Bvxb7+WkrGs6TH3rZt5c+nn07RMgFwP11guW3btmZi1IiIiMBqKbKtNr18+XKPrChvM3DgwGitS9pSpHOWaP5Pcl9UnQ1XE6y1Hto6pJsuAPn333+bIE0DPg3atCstEGiUrtdLl1DQLsJAFOh1DMj6NW0qd3LlktRO/pi688orUnTMGCkqgSMgr6ED6uf/ojxUR1tPjyd5LShyZbVpG13ZOmaLkN7W/XEJCwszW0x6oZJ7sTQYatKkiRltpgGS7XyaXG3LkdLutUB747vjtfN1gV7HgKufLuwaEiKWf7vOLPpHioikvntXUgdSPQP5GsZA/fxfqJvrmBKvl9cSrW2rTeuiqLatSpUqplVFf48ZEKkaNWrIypUro+3TaFT3e4MmfY8bN06WLVtmZsHV0Whq2LBhcuPGDTNdANP8AykQEM2ebYbka/L1H7pGmiZh623tVtP7AcCXgyLbatOOW8zVpnWou3Z/ObbM6Nw/Gojo5IiaL7Rlyxbp0aOHt6phuuZ0SoAOHTrYA7mRI0ea8rmyfhKAJPrnHxFdxuOJJ+T2rl2yr00b81Nvm5mt9X4A8IfRZ/HRfB3bYqpKJ0fUBGmdB0hbZnSNMB15ZguivKV06dLy/vvvm35UbR16/vnnA75ZFPAZGTNaZ7XWP0pso1Ny5tRZVLVP3rofAPwtKIq52rSz1adbtWplNgCwiyvwISAC4E+TNwIAAPgCgiIAAACCIgAAACuCIgAAAIIiAAAAK4IiAAAAgiIAAAArgiIAAACCIgAAACuCIgAAAIIiAAAAK4IiAAAAgiIAAAArgiIAAACCIgAAACuCIgAAAIIiAAAAK4IiAAAAgiIAAAArgiIAAACCIgAAACuCIgAAAIIiAAAAK4IiAAAAgiIAAAArgiIAAACCIgAAACuCIgAAAIIiAAAAqzTiot69ezvdHxISIunSpZOiRYtK8+bNJVu2bK6eGgAAwH+Cou3bt8u2bdvkzp07Urx4cbPvzz//lNSpU0uJEiXkww8/lD59+si6deukVKlSnigzAACA97vPtBWofv36cvLkSdm6davZjh8/Lg0aNJA2bdrIiRMn5OGHH5ZXXnklwXNNnjxZypUrJxEREWarUaOGLFmyJM7jZ8yYYVqkHDdtnQIAAEjxlqKxY8fK8uXLTRBjkzlzZhkyZIg0bNhQevbsKW+++ab5PSH58uWT0aNHy/333y8Wi0Vmzpxpgi5tjSpdurTTx+jz7tu3z35bAyMAAIAUD4ouX74sZ8+ejdU19vfff8uVK1fM71myZJFbt24leK7HHnss2u0RI0aY1qNNmzbFGRRpEJQ7d25Xiw0AAODeoEhbcjp37izjxo2TBx54wOzbvHmz9O3bV1q0aGFu//rrr1KsWDGXzqs5SnPnzpXr16+bbrS4XLt2TQoWLCh3796VSpUqyciRI+MMoNTNmzfNZmML3KKioszmTrbzufu8viLQ6xcMdaR+/i/Q60j9/F+Uh+qYEq9ZiEX7rVygQYnmC3322Wdy+/Ztsy9NmjTSqVMnGT9+vGTMmFF27Nhh9leoUCHB8+3cudMEQTdu3JBMmTLJrFmzpGnTpk6P3bhxo+zfv9/kIWmL1TvvvCNr166V3bt3m644Z7Rbb+jQobH26/NkyJDBlaoDAAAviYyMlLZt25rvf8cUHq8GRY7B0aFDh8zvRYoUMQFNUmg329GjR00l582bJ1OnTpU1a9YkauSaRo0lS5Y0Cd7Dhw9PdEtR/vz55dy5c25/UbU8mm+lSeehoaESaAK9fsFQR+rn/wK9jtTP/0V5qI76/Z0jRw6PBkUud5/ZaBCkLTbJlTZtWjO3kapcubLpips4caJMmTIlwcfqi12xYkU5cOBAnMeEhYWZzdljPfWG9OS5fUGg1y8Y6kj9/F+g15H6+b9QN9cxJV4vl4MizfnREWMrV640Cdea2+PI1nqUVHo+x5adhPKQtPstru42AAAAjwVFXbt2Nd1bHTp0kDx58iRrSPzAgQOlSZMmUqBAAbl69arJ81m9erUsW7bM3N+xY0fJmzevjBo1ytweNmyYVK9e3bQsXbp0yUwPcOTIEVMmAACAFA2KdHLFH374QWrVqiXJpS1NGvicOnXKzHWk3XEaEGk/pNJco1Sp/j+/5MWLF6Vbt25y+vRpyZo1q+lu27BhAzNnAwCAlA+KNBhx17pm06ZNi/d+bTVypKPbdAMAAPD6Mh86yktnrNahcQAAAEHbUqSTNh48eFBy5colhQoVipUNrovFAgAABHxQZJu1GgAAIKiDosGDB3umJAAAAP6UUwQAABC0LUU62uzPP/8002vr6LP45ia6cOGCO8sHAADgO0GRDoMPDw+3/56cCRsBAAD8Nijq1KmT/fdnnnnGk+UBAADwj5yi1KlTm5moYzp//ry5DwAAICiCIovF4nS/LuKqK94DAAAE9JD89957z/zUfKKpU6dKpkyZoq1Wv3btWilRooRnSgkAAOArQZFtzTFtKfroo4+idZVpC5HObq37AQAAAjooOnz4sPlZt25d+eabb8zQfAAAgKCd0fqnn37yTEkAAAD8KShSx48fl0WLFsnRo0fl1q1b0e5799133VU2AAAA3w2KVq5cKY8//rgUKVJE/vjjDylTpoz89ddfJteoUqVKniklAACArw3JHzhwoPTt21d27twp6dKlk/nz58uxY8ekdu3a0qpVK8+UEgAAwNeCor1790rHjh3N72nSpJF//vnHDM8fNmyYjBkzxhNlBAAA8L2gKGPGjPY8ojx58sjBgwft9507d869pQMAAPDVnKLq1avLunXrpGTJktK0aVPp06eP6UrTYfp6HwAAQFAERTq67Nq1a+b3oUOHmt+//vpruf/++xl5BgAAgiMo0uU8dDh+uXLl7F1pzGINAACCLqdIl/Zo2LChXLx40XMlAgAA8IdEa52X6NChQ54pDQAAgL8ERW+99ZaZp+j777+XU6dOyZUrV6JtAAAAQZForSPOlM5qHRISYt+vM1rrbc07AgAA8DcsCAsAAJCUoEiX8wAAAJBgzykCAAAIRARFAAAABEUAAAA+EBRNnjzZzI4dERFhtho1asiSJUvifczcuXOlRIkSki5dOilbtqwsXrw4xcoLAAACV7KDolu3btnXQnNVvnz5ZPTo0bJ161bZsmWLPPLII9K8eXPZvXu30+M3bNggbdq0kS5dusj27dulRYsWZtu1a1cyawEAAIKdS0HR9OnT5aWXXpIvv/zS3B44cKCEh4dL5syZpUGDBnL+/HmXnvyxxx4z8x7pYrLFihWTESNGSKZMmWTTpk1Oj584caI0btxY+vXrJyVLlpThw4dLpUqV5IMPPnDpeQEAAJI8JF8DFt1q1aols2bNknXr1snChQtl2LBhkipVKnnvvffk9ddfN11iSaGTPmrX2PXr1003mjMbN26U3r17R9vXqFEjU4643Lx502w2tlm3o6KizOZOtvO5+7y+ItDrFwx1pH7+L9DrSP38X5SH6pgSr1mIRaeiTgRtzdEASLuvtKurWrVqMmfOHHnyySfN/ZoL9Pzzz8uRI0dcKsDOnTtNEHTjxg3TSqQBl23W7JjSpk0rM2fONGWw+fDDD2Xo0KFy5swZp48ZMmSIuT8mfZ4MGTK4VFYAAOAdkZGR0rZtW7l8+bLJQ/ZqS9HRo0flwQcfNL9XqVJF0qRJYxaHtdGEaV0LzVXFixeXHTt2mErOmzdPOnXqJGvWrJFSpUqJO2gXn2PrkrYU5c+fXxo2bOj2F1Wj2OXLl5uuxNDQUAk0gV6/YKgj9fN/gV5H6uf/ojxUx5RYXzWNK5UMCwuL1mrjWFkNkpKy7pmep2jRoub3ypUry+bNm03u0JQpU2Idmzt37lgtQnpb98dFy+xYbhstu6fekJ48ty8I9PoFQx2pn/8L9DpSP/8X6uY6psTr5dIyH3v27JHTp0+b37XX7Y8//rCPPDt37pxbCnT37t1oOUCOtJtt5cqV0qtXL/s+jUbjykECAADwSFBUr149EwzZPProo+ZnSEiI2a8/Xe3aatKkiRQoUECuXr1q8nxWr14ty5YtM/d37NhR8ubNK6NGjTK3e/bsadZeGzdunDRr1kxmz55t8ps+/vhjl54XAAAgyUHR4cOHxd3Onj1rAh/NRdJh/ZqXpAGR9kPa8ph0ZJtNzZo1TeCko9wGDRpkkr915JljbhMAAIBHg6KCBQuKu02bNi3e+7XVKKZWrVqZDQAAwJ1Y+wwAAICgCAAAwIqgCAAAgKAIAAAgGUHR7du3ZcWKFWaCRR1Kr06ePGmfswgAACCg5ylSuraZrlSvw+V1kkUdPh8eHi5jxowxtz/66CPPlBQAAMCXWop0AkVd++zixYuSPn16+/4nnnjCzDYNAAAQFC1FP//8s2zYsMGsWeaoUKFCcuLECXeWDQAAwHdbinRtMmcLvx4/ftx0owEAAARFUNSwYUOZMGGC/baud6YJ1oMHD5amTZu6u3wAAAC+2X2mi7E2atRISpUqJTdu3JC2bdvK/v37JUeOHPLVV195ppQAAAC+FhTly5dPfvvtN7NC/e+//25aibp06SLt2rWLlngNAAAQ0EGReVCaNNK+fXv3lwYAAMCXg6JFixZJkyZNJDQ01Pwen8cff9xdZQMAAPCtoKhFixZy+vRpyZkzp/k9Lpp07WxkGgAAQEAERToM39nvAAAAQTsk/9ixY54pCQAAgD8FRTpzde3ateWTTz4xS30AAAAEZVC0ZcsWqVq1qgwbNkzy5MljcozmzZtnFoMFAAAImqCoYsWKMnbsWDl69KgsWbJE7rnnHnnuueckV65c0rlzZ8+UEgAAwNeCIseRZnXr1jXdaCtWrJDChQvLzJkz3Vs6AAAAXw+KdAHYt99+WypUqGC60zJlyiSTJk1yb+kAAAB8dUbrKVOmyKxZs2T9+vVSokQJs7zHt99+KwULFvRMCQEAAHwxKHrrrbekTZs28t5770n58uU9UyoAAABfD4o0wVrziQAAAII6KNKA6NKlSzJt2jTZu3ev2VeqVCnp0qWLZM6c2RNlBAAA8M15iu677z4ZP368XLhwwWz6u+7btm2bZ0oJAADgay1Fr7zyijz++ONmKH6aNNaH3759W7p27Sq9evWStWvXeqKcAAAAvhUUaUuRY0BkTpImjfTv31+qVKni7vIBAAD4ZvdZRESESbZ2tlBseHi4u8oFAADg20FR69atTVL1119/bQIh3WbPnm26z3SoPgAAQFAERe+88460bNlSOnbsKIUKFTLbM888I0899ZSMGTPGpXONGjVKHnjgAdPClDNnTrO47L59++J9zIwZM8wIOMctXbp0rlYDAAAgeTlFadOmlYkTJ5qA5uDBg2afjjzLkCGDq6eSNWvWSPfu3U1gpMnagwYNkoYNG8qePXskY8aM8XbhOQZPzJsEAABSPCiy0SCobNmyyXrypUuXxmoF0hajrVu3ysMPPxzn4zQIyp07d7KeGwAAIElBUefOnRN13KeffipJdfnyZfMzW7Zs8R537do1s9ba3bt3pVKlSjJy5EgpXbq002Nv3rxpNpsrV66Yn1FRUWZzJ9v53H1eXxHo9QuGOlI//xfodaR+/i/KQ3VMidcsxGKxWBJzYKpUqUwgUrFiRYnvIQsWLEhSQTTA0fmPdLbsdevWxXncxo0bZf/+/VKuXDkTRGmOk86NtHv3bsmXL1+s44cMGSJDhw6NtV8XtU1Klx8AAEh5kZGR0rZtW/Pdr2k0Xg2KNPfnq6++MoHRs88+K+3bt0+wRccVL7zwgixZssQERM6Cm/gix5IlS5qRb8OHD09US1H+/Pnl3Llzbn9RtSzLly+XBg0aSGhoqASaQK9fMNSR+vm/QK8j9fN/UR6qo35/58iRw6NBUaK7zyZNmiTvvvuufPPNN6aLbODAgdKsWTMzPF+To5OT7NyjRw/5/vvvTYuPKwGR0hdcW68OHDjg9P6wsDCzOXucp96Qnjy3Lwj0+gVDHamf/wv0OlI//xfq5jqmxOvl0pB8DS60RUYjQB0hpnk8L774ohmWr3k+rtJGKg2ItMtt1apVUrhwYZfPcefOHdm5c6fkyZPH5ccCAAAke/SZ5hhp65AGNhqYJIV2yWluz7fffmvmKjp9+rTZnzlzZkmfPr35XedDyps3r5kCQA0bNkyqV68uRYsWNflHY8eOlSNHjpjJIwEAAFKkpUhzczSvSPsJixUrZlpoPvjgA7PsR6ZMmVx+8smTJ5u+wTp16piWHtums2Xb6LlPnTplv33x4kXp1q2bySNq2rSp6WPcsGGDlCpVyuXnBwAAcLmlSLvJdDkPTVLW4fkaHGnCU3IkJsd79erV0W6PHz/ebAAAAF4Jij766CMpUKCAFClSxMxErZszmogNAAAQsEGR5vawnAYAAJBgD4p0CQ4AAIBA5VKiNQAAQKAiKAIAACAoAgAAsCIoAgAAICgCAACwIigCAAAgKAIAALAiKAIAACAoAgAAsCIoAgAAICgCAACwIigCAAAgKAIAALAiKAIQkM6fPy85c+aUv/76K0Wf99atW1KoUCHZsmVLij5vIOIaIqURFAEISCNGjJDmzZubLzfbF2zjxo3l3nvvlbCwMMmfP7/06NFDrly54tJ5R40aJQ888ICEh4ebL+wWLVrIvn377PenTZtW+vbtK6+++qrb6xTs19CRXs98+fJJSEiIXLp0yaXzcg0RF4IiAAEnMjJSpk2bJl26dLHvS5UqlfmCXbRokfz5558yY8YMWbFihTz//PMunXvNmjXSvXt32bRpkyxfvlyioqKkYcOGcv36dfsx7dq1k3Xr1snu3bvdWq9gv4aOdH+5cuWSdG6uIeJCUAQg4CxZssS0BlWvXt2+L2vWrPLCCy9IlSpVpGDBglKvXj158cUX5eeff3bp3EuXLpVnnnlGSpcuLeXLlzfB1dGjR2Xr1q3RnqtWrVoye/Zst9Yr2K+hzeTJk03rkLbmJAXXEHFJE+c9AOCn1q9fL5UrV473mJMnT8o333wjtWvXTtZzXb582fzMli1btP1Vq1Z1OeBCwtdwz549MmzYMPnll1/k0KFDbnkuriFsaCkCEHCOHDlicoecadOmjWTIkEHy5s0rERERMnXq1CQ/z927d6VXr16mRaFMmTLR7tPn13LAfdfw5s2b5vqNHTtWChQo4Jbn4RrCEUERgIBz48YNSZcundP7xo8fL9u2bZNvv/1WDh48KL17907y82heyq5du5x2saRPn97kxcB913DgwIFSsmRJad++vdueh2sIRwRFAAJO9uzZ5eLFi07vy507t5QoUUIef/xxmTJlislPOXXqlMvPoSPXvv/+e/npp5/MKKiYLly4IPfcc0+Syg/n13DVqlUyd+5cSZMmjdk0L0zlyJFDBg8e7PJzcA0REzlFAAJOhQoV5KuvvkpU14mtWyaxLBaLvPTSS7JgwQJZvXq1FC5c2Olx2vpQsWJFF0qNhK7h/Pnz5Z9//rHf3rx5s3Tu3Nnk/dx3332JPjfXEHGhpQhAwGnQoIEZSu3Y0rB48WKZPn26+aLTyQB/+OEHMxxfc0mczYMTX3fLF198IbNmzTLz3Jw+fdpsjl/WSr+odZg33HcNNfDRvB/bZgtmtEtN5xtKLK4h4kJQBCDglC1bVipVqiRz5syJlh/yySefyIMPPmi+RF955RXThabdJzYaLOlkgNp6EBftbtPRSnXq1JE8efLYt6+//tp+zMaNG80xTz31lAdrGXzXMDG4hkgOus8ABKQ333xT+vXrJ926dTMTN9atW1c2bNgQ72MOHz4sWbJkMXPXxNf1kpAJEyaY59ZADO67hjFpUBPzenANkRwERQACUrNmzWT//v1y4sQJs6RHYmgX26BBg8zEfclZN0tbObQlCsnDNURKIygCELB0/hlX6Pw3yaXrZr3++uvJPg+suIZISeQUAfB7OsGeLvlQpmRJc7tR/fomkfbOnTveLhoSSbs2Wz/9tNz/b/J0p06dzNpjQNAERQmtVBwXnadC5xnRib20iVObSwEEp19//VXKlykjn773ntS+bF3xPvWu3dKhQwd5qmVLs9gnfNv7779vRgFuXfSdPHnrltn3+5Kl8tBDD8nEiRO9XTwEEa8GRYlZqdjZXxM6zbuukLx9+3YTSOmmw2wBBBfN/WjZvLkUvX1H1me/R16PyGz2T8uSVWZky25Glr377rveLibiof+Pv/zyy/JcxkyyOlt26RluvYY/ZM0mz2fMZLrPHBdqBQI2KErMSsUx6V8NjRs3NqMCdFjt8OHDzbDNDz74IEXLDsD7dPK9E6dPy9vhERIRY3RS/XTp5cl06WXSxIl0o/l4K1HesDB5LSKzGUpvo78PjMgs+cLC+P8dwZloHddKxY507oiYaxU1atRIFi5c6PR4nanWcbbaK1eszevaKuXuZnXb+QK1uT7Q6xcMdQy0+umMxiWzZpWi4eGiYc+dsDCz3/azaZYssujiBTl27JhZADYQBNo13LF5szTOFK4TScW6hqn1GoZHyC+//how9Q2065eSdUyJ1yzEkpgJG1KATrevE6ldunQp3uQ6HRUwc+ZM04Vm8+GHH8rQoUPlzJkzsY4fMmSIuS8mnclUV8oGAAC+Txfnbdu2rWlAiYiICOyWIttKxe4ebaCrKju2LGlLkc53oblL7n5RNYrV3Cidnj40NFQCTaDXLxjqGGj1W7lypbRs2VK+yJZDqoSFmdaF30eNkHIDX5PUN29K90sX5WS+vLJx8+ZoXTP+LNCu4WuvvSZfTJkiP2XLIelTpYp2DW/duCGPnP9b2nTrZgbmBIJAu34pWUdbT48n+URQZFupeO3atU5XKo65wnXMFiG9rfudCQsLM1tMeqE89Yb05Ll9QaDXLxjqGCj10z9uihYqJH0OHZKZmbOIfQWzGzdk6vlz8v2Vy/LpO2NNC3OgCZRrqOvPvT9xovQ8c1omZM4itvZ7XYes75nTcv52lDkmEOoaiNcvJeuYEq+XVxOttedOAyJNlly1alWcKxU7qlGjhvnr0JFGpLofQHDRpR++/eEHSZMnj9Q5e0a6XLxg9je4cF6GX7lsBmToYA74riJFisjc+fNl7d07UuXcWen17zWsff5v+enObZkzb54ULVrU28VEkPBqUJSYlYo7duxousBsevbsaUatjRs3Tv744w+TM7RlyxYTXAEIPvrH1I5du+TjTz6RdDWqm331/9PazF/09ttvB0y3WSB79NFHZf/Bg9Jn0CC5VrGC2de9d2+zT3NNgaAIihKzUrEO0T916pT9ds2aNU0Q9fHHH5th/PPmzTMjz8qUKeOlWgDwtowZM0rXrl3lm39Hoeowb50YFv5DRwfqoJgfli615xollE4BBFROUWIGvq1evTrWvlatWpkNAADAXVj7DAAAgKAIAADAiqAIAACAoAgAAMCKoAgAAICgCAAAwIqgCAAAgKAIAADAiqAIAACAoAgAAMCKoAgAAICgCAAAwIqgCAAAgKAIAADAiqAIAACAoAgAAMCKoAgAAICgCAAAwIqgCAAAgKAIAADAiqAIAACAoAgAAMCKoAgAAICgCAAAwIqgCAAAgKAIAADAiqAIAACAoAgAAMCKoAgAAICgCAAAwIqgCAAAwNtB0dq1a+Wxxx6Te++9V0JCQmThwoXxHr969WpzXMzt9OnTKVZmAAAQmLwaFF2/fl3Kly8vkyZNculx+/btk1OnTtm3nDlzeqyMAAAgOKTx5pM3adLEbK7SIChLliweKRMAAAhOXg2KkqpChQpy8+ZNKVOmjAwZMkRq1aoV57F6nG42V65cMT+joqLM5k6287n7vL4i0OsXDHWkfv4v0OtI/fxflIfqmBKvWYjFYrGID9DcoAULFkiLFi3i7TbTvKIqVaqYQGfq1Kny+eefyy+//CKVKlVy+hgNmoYOHRpr/6xZsyRDhgxurQMAAPCMyMhIadu2rVy+fFkiIiI88hx+FRQ5U7t2bSlQoIAJjhLbUpQ/f345d+6c219UjWKXL18uDRo0kNDQUAk0gV6/YKgj9fN/gV5H6uf/ojxUR/3+zpEjh0eDIr/sPnNUtWpVWbduXZz3h4WFmS0mvVCeekN68ty+INDrFwx1pH7+L9DrSP38X6ib65gSr5ffz1O0Y8cOyZMnj7eLAQAA/JxXW4quXbsmBw4csN8+fPiwCXKyZctmusQGDhwoJ06ckM8++8zcP2HCBClcuLCULl1abty4YXKKVq1aJT/++KMXawEAAAKBV4OiLVu2SN26de23e/fubX526tRJZsyYYeYgOnr0qP3+W7duSZ8+fUygpEnS5cqVkxUrVkQ7BwAAgN8FRXXq1JH48rw1MHLUv39/swEAALib3+cUAQAAuANBEQAAAEERAACAFUERAAAAQREAAIAVQREAAABBEQAAgBVBEQAAAEERAACAFUERAAAAQREAAIAVQREAAABBEQAAgBVBEQAAAEERAACAFUERAAAAQREAAPCU8+fPS86cOeWvv/5K8eeuXr26zJ8/36XHEBQBAACPGDFihDRv3lwKFSpk3xcSEhJrmz17tkvnXbt2rTz22GNy7733mscvXLgw1jGvv/66DBgwQO7evZvo8xIUAQAAt4uMjJRp06ZJly5dYt03ffp0OXXqlH1r0aKFS+e+fv26lC9fXiZNmhTnMU2aNJGrV6/KkiVLEn3eNC6VAgAAIBE0GAkLCzPdWDFlyZJFcufOLUmlAY9u8UmdOrU0bdrUtEI1a9YsUeelpQgAALjd+vXrpXLlyk7v6969u+TIkUOqVq0qn376qVgsFvcXQMSc/+eff0708bQUAQAAtzty5IjJ+Ylp2LBh8sgjj0iGDBnkxx9/lBdffFGuXbsmL7/8stvLoM9/7Ngxk1eUKlXC7UAERQAAwO1u3Lgh6dKli7X/jTfesP9esWJFkx80duxYjwRF6dOnNwHRzZs3ze8JofsMAAC4Xfbs2eXixYsJHletWjU5fvy4CVzc7cKFC5IxY8ZEBUSKoAgAALhdhQoVZM+ePQket2PHDsmaNatJyna3Xbt2mdaoxKL7DAAAuF2DBg3MXEHaWqRBj/ruu+/kzJkzZkSadq0tX75cRo4cKX379nXp3JqDdODAAfvtw4cPm+AqW7ZsUqBAAft+TbJu2LBhos9LSxEAAHC7smXLSqVKlWTOnDn2faGhoWZuoRo1apiWpClTpsi7774rgwcPth+js1/rhIyrV6+O89xbtmwxLUC2VqDevXub39988037MSdOnJANGzbIs88+m+gy01IEAAA8QoOUfv36Sbdu3czor8aNG5stPtrqo/MY6eSMcalTp06Cw/jfe+89eeaZZyRfvnyJLi9BEQAA8AidNHH//v2m1SZ//vyJeszixYtl0KBB9i63pNI117QFyRUERQAAwGN69erl0vE6PN8d+vTp4/JjyCkCAADJcufOHfnyyy/loZo1pfC/LUKvvfaayQ/yJ14NihKzym1MmniliVs6dK9o0aIyY8aMFCkrAACI7fbt29LqySelffv2ErJjh3S6a831+WLKFKlQtqxs2rRJ/IVXg6LErHIbM/lK+yfr1q1rht5pk1zXrl1l2bJlHi8rAACIbfz48Wao/fRs2eWrrNnlv+ERZv/ybDmk2O070rJ5c7l165b4A6/mFCVmlVtHH330kRQuXFjGjRtnbpcsWVLWrVtnLkijRo08WFIAABCTLqExaeJEeTJdemmQLvqs0eGpUsnb4RFS9+wZ+eabb+Q///mP+Dq/SrTeuHGj1K9fP9o+DYbiS+LSacMdpw6/cuWK+RkVFWU2d7Kdz93n9RWBXr9gqCP183+BXkfq519OnTolZy9ckEZZs8mdf9c5u/PvzNT6s4iIlL4dJb/++qs8+eSTyXqulHjNQiwJDfRPIZpTtGDBAmnRokWcxxQrVsxMwjRw4MBoQ/e0Sy0yMtLp2iZDhgyRoUOHxto/a9Yss0IvAADwffo937ZtW7l8+bJERFi76IK6pSgpNIBynKdAW4p0rgSd9tvdL6pGsTpluU5trrN2BppAr18w1JH6+b9AryP18y8Wi0VqVasmuY4ekw+zZLW3EP0+aoSUG/ia/H71irQ5f07mzZtn6pwctp4eT/KroCh37txmzRRHeluDm7hWwNVRas4WmdM3o6fekJ48ty8I9PoFQx2pn/8L9DpSP//Ro1cvM3P0J7dvS7eMmST1v/uPXb8mr/x9VooUKmRmsdYZrZMjJV4vv5qnSNdKWblyZbR9GnHrfgAAkPI6duwor776qrx15bLUPH9OBly6aPY3+fushOTOLYsWL052QJRSvFpKXeVWh9br5rjK7dGjR+1dX/pi2zz//PNy6NAh6d+/v/zxxx/y4YcfmoXmXnnlFa/VAQCAYBYSEiKjR4+WzZs3S+MO7eV06VJm/8T33pPfd++WIkU03do/eDUoSmiVW81qtwVISofj//DDD6Z1SOc30qH5U6dOZTg+AABeVqVKFfOdvOzfHp1OnTpJxowZxZ94NacooVVunc1WrY/Zvn27h0sGAACCjX908gEAAHgYQREAAABBEQAAgBVBEQAAAEERAACAFUERAAAAQREAAIAVQREAAABBEQAAgA/MaO0Nthm0r1y54vZzR0VFSWRkpDl3oKx+HEz1C4Y6Uj//F+h1pH7+L8pDdbR9b8e3EkZyBV1QdPXqVfMzf/783i4KAABIwvd45syZxRNCLJ4MuXzQ3bt35eTJkxIeHm5W9nUnjWI12Dp27JhERERIoAn0+gVDHamf/wv0OlI//3fFQ3XUcEUDonvvvVdSpfJM9k/QtRTpC5kvXz6PPoe+CQL1zR4M9QuGOlI//xfodaR+/i/CA3X0VAuRDYnWAAAABEUAAABWBEVuFBYWJoMHDzY/A1Gg1y8Y6kj9/F+g15H6+b8wP65j0CVaAwAAOENLEQAAAEERAACAFUERAAAAQREAAIAVQVEirV27Vh577DEzk6bOhL1w4cIEH7N69WqpVKmSycAvWrSozJgxQwKpjlo/PS7mdvr0afFFo0aNkgceeMDMZp4zZ05p0aKF7Nu3L8HHzZ07V0qUKCHp0qWTsmXLyuLFiyVQ6qfvyZjXT+vpiyZPnizlypWzTwhXo0YNWbJkSUBcu6TW0Z+unzOjR482Ze7Vq1dAXUdX6udv13DIkCGxyqvXJlCuH0FRIl2/fl3Kly8vkyZNStTxhw8flmbNmkndunVlx44d5kPRtWtXWbZsmQRKHW30i/fUqVP2Tb+QfdGaNWuke/fusmnTJlm+fLlZtLBhw4am3nHZsGGDtGnTRrp06SLbt283gYZuu3btkkCon9IvX8frd+TIEfFFOhO9fsls3bpVtmzZIo888og0b95cdu/e7ffXLql19KfrF9PmzZtlypQpJgiMjz9eR1fq54/XsHTp0tHKu27dusC5fjokH67Rl23BggXxHtO/f39L6dKlo+1r3bq1pVGjRpZAqeNPP/1kjrt48aLFH509e9aUf82aNXEe8/TTT1uaNWsWbV+1atUs//3vfy2BUL/p06dbMmfObPFXWbNmtUydOjXgrl1i6+iv1+/q1auW+++/37J8+XJL7dq1LT179ozzWH+8jq7Uz9+u4eDBgy3ly5dP9PH+dv1oKfKQjRs3Sv369aPta9SokdkfaCpUqCB58uSRBg0ayPr168VfXL582fzMli1bQF7HxNRPXbt2TQoWLGgWcEyoVcJX3LlzR2bPnm1awbSLKdCuXWLr6K/XT1s0tSU95vUJlOvoSv388Rru37/fpFkUKVJE2rVrJ0ePHg2Y6xd0C8KmFM2ryZUrV7R9eltXD/7nn38kffr04u80EProo4+kSpUqcvPmTZk6darUqVNHfvnlF5NL5cvu3r1rujRr1aolZcqUcfk6+mrelKv1K168uHz66aemiV+DqHfeeUdq1qxp/lP29MLJSbFz504TINy4cUMyZcokCxYskFKlSgXUtXOljv52/ZQGetu2bTPdS4nhb9fR1fr52zWsVq2ayYPScmvX2dChQ+Whhx4y3WGaz+jv14+gCEmmHwrdbPSDfPDgQRk/frx8/vnn4sv0Lzn9EMfXF+7PEls//fJ1bIXQa1iyZEmTCzF8+HDxNfp+0xw9/fKYN2+edOrUyeRSxRU0+CNX6uhv1+/YsWPSs2dPk/Pmy8nEKVk/f7uGTZo0sf+ugZwGSdrKNWfOHJM35O8Iijwkd+7ccubMmWj79LYm1AVCK1Fcqlat6vOBRo8ePeT77783o+0S+kssruuo+wOhfjGFhoZKxYoV5cCBA+KL0qZNa0ZyqsqVK5u/xidOnGi+QALh2rlaR3+7fppAfvbs2WgtydpNqO/VDz74wLQ4p06d2m+vY1Lq52/XMKYsWbJIsWLF4iyvP10/RU6Rh2jkv3Llymj79K+H+HIDAoH+havdar5I88c1YNDuiFWrVknhwoUD6jompX4x6X/g2n3jq9fQWTehftH4+7VLah397frVq1fPlE//n7Bt2v2ueSn6u7OAwZ+uY1Lq52/X0Fk+lPYQxFVef7p+hrczvf2FjibYvn272fRle/fdd83vR44cMfcPGDDA0qFDB/vxhw4dsmTIkMHSr18/y969ey2TJk2ypE6d2rJ06VJLoNRx/PjxloULF1r2799v2blzpxlhkSpVKsuKFSssvuiFF14wozxWr15tOXXqlH2LjIy0H6P103rarF+/3pImTRrLO++8Y66jjrwIDQ019Q2E+g0dOtSybNkyy8GDBy1bt261/Oc//7GkS5fOsnv3bouv0XLrSLrDhw9bfv/9d3M7JCTE8uOPP/r9tUtqHf3p+sUl5uisQLiOrtTP365hnz59zP8x+h7Va1O/fn1Ljhw5zGjXQLh+BEWJZBt+HnPr1KmTuV9/6ps/5mMqVKhgSZs2raVIkSJm6GUg1XHMmDGW++67z3yAs2XLZqlTp45l1apVFl/lrG66OV4XrZ+tvjZz5syxFCtWzFxHnWbhhx9+sARK/Xr16mUpUKCAqVuuXLksTZs2tWzbts3iizp37mwpWLCgKes999xjqVevnj1Y8Pdrl9Q6+tP1S2zQEAjX0ZX6+ds1bN26tSVPnjymvHnz5jW3Dxw4EDDXL0T/8XZrFQAAgLeRUwQAAEBQBAAAYEVQBAAAQFAEAABgRVAEAABAUAQAAGBFUAQAAEBQBMDbQkJCZOHCheLvhgwZIhUqVPB2MQAkA0ERALtnnnnGBCnPP/98rPu6d+9u7tNj3OnUqVPRVt5OjkaNGpn1pXQR1ZQO5Pr27RtrjScA/oWgCEA0+fPnl9mzZ8s///xj33fjxg2ZNWuWFChQwO3Pp6tlh4WFJfs8R48elQ0bNphFcT/99FOXH68Lceriq0mVKVMmyZ49e5IfD8D7CIoARFOpUiUTGH3zzTf2ffq7BkQVK1aMdqyu3v7yyy9Lzpw5JV26dPLggw/aW2k0wMiXL59Mnjw52mO2b98uqVKlkiNHjjhtdTl27Jg8/fTTkiVLFsmWLZs0b95c/vrrrwTLPX36dHn00UflhRdekK+++ipaUOfMjBkzzHMsWrRISpUqZQIzDay0/A0aNJAcOXJI5syZpXbt2rJt2zb74woVKmR+PvHEE6bsttsxu8+0Ra1FixbyzjvvmBXENWDS1raoqKhorWTNmjWT9OnTS+HChU3gqeebMGFCgvUF4H4ERQBi6dy5swkybLTl5dlnn411XP/+/WX+/Pkyc+ZMEzgULVrUdGFduHDBBD5t2rQxX/SOvvzyS6lVq5YULFgw1vk0YNDHh4eHy88//yzr1683LTCNGzeWW7duxVleXcJRy9u+fXspUaKEKce8efMSrGdkZKSMGTNGpk6dKrt37zbB3dWrV6VTp06ybt062bRpk9x///3StGlTs1/Zgj59Pg1q4uuq++mnn+TgwYPmp75GGojpZtOxY0c5efKkrF692ryOH3/8sZw9ezbBcgPwEG+vSAvAd+jq1s2bN7ecPXvWEhYWZvnrr7/Mli5dOsvff/9t7rOtgH3t2jVLaGio5csvv7Q//tatW5Z7773X8vbbb5vb27dvt4SEhFiOHDlibt+5c8esrD158mT7Y/S/oQULFpjfP//8c0vx4sUtd+/etd9/8+ZNS/r06S3Lli2Ls9y6kryuKh8VFWVujx8/3qzWHZ/p06eb596xY0e8x2mZw8PDLd99953TMtsMHjzYUr58+Wivpa54f/v2bfu+Vq1amVXF1d69e815Nm/ebL9///79Zp+WH0DKo6UIQCz33HOP6dbRVg1tEdHftTvJkbaAaMuOtvrYhIaGStWqVWXv3r3mtnYnlSxZ0t5atGbNGtMS0qpVK6fP+9tvv8mBAwdMS5G2EOmmXWia06TPFxdtyWrdurWkSZPG3NYWKm1liu8xKm3atFKuXLlo+86cOSPdunUzLUTafRYRESHXrl0zXWuuKl26tEn8ttFuNFtL0L59+0x5tbvSRlu4smbN6vLzAHAP6/8gAOCkC02TltWkSZOSfJ527dqZoGjAgAHmp3aFxZWQrMFH5cqVTRebs0DNGe2qW7BggQnQHPOXNHFag6URI0bEWTbN5dG8IEfadXb+/HmZOHGi6eLTXKMaNWrE230XFw0SHelzJSeZG4Bn0VIEwClbHo8tzyem++67z7S0aIuMjR6rOTaauGzTtm1b2bVrl2zdutXk+WiQFBdtNdm/f7/J7dFWE8dNW22c0QBKE7q1lWnHjh32bdy4caalS4MjV2h9NHlc84i0pUeDonPnzsUKdlw9b0zFixeX27dvm8RzG20lu3jxYrLOCyDpCIoAOKXdPtoNtmfPnmhdQDYZM2Y0I7369esnS5cuNcdpt5MmL3fp0sV+nI6mqlmzptmngcTjjz8e53NqwKTddDriTBOtDx8+bJKQNUg5fvy408dMmzZNnnrqKSlTpky0TZ9Pgxktmyu02+zzzz83df/ll19MmbRFyZHWSeckOn36dJKDGE0Ir1+/vjz33HPy66+/muBIf3fWegUgZRAUAYiT5tPoFpfRo0fLk08+KR06dDCtPNrSsWzZslh5MRpYaEuODmOPGWA4ypAhg6xdu9YM/2/ZsqXJR9LgRnOKnJVDW5/0vFqGmLRlqV69eiZocoUer4GO1kfrZZtywJG2Qi1fvtxMXRBzmgJXfPbZZ5IrVy55+OGHzWujQaXmU+n0BgBSXohmW3vheQEAMWhrmAZaK1asMAEdgJRFUAQAXrJq1SqTXF62bFkz55HO+3TixAn5888/YyVpA/A8Rp8BgJdoYvqgQYPk0KFDpttMc680cZyACPAOWooAAABItAYAALAiKAIAACAoAgAAsCIoAgAAICgCAACwIigCAAAgKAIAALAiKAIAACAoAgAAEON/b+RzzTBx/LIAAAAASUVORK5CYII=",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "这个用户可能偏好喜剧片\n"
     ]
    }
   ],
   "source": [
    "# 5. 数据可视化\n",
    "plt.title(\"User Movie Preference\", size=15)\n",
    "plt.xlabel(\"Movie A rating\")\n",
    "plt.ylabel(\"Movie B rating\")\n",
    "plt.grid()\n",
    "\n",
    "# 绘制原始样本点，要求不同的影片喜好类别用不同的颜色标记\n",
    "plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.Set1, edgecolor='k')\n",
    "\n",
    "# 绘制新数据点，用红色标记，大小为8\n",
    "plt.plot(new_user[0, 0], new_user[0, 1], 'r*', markersize=8)\n",
    "\n",
    "# 新数据最近邻索引为第一个最近邻的索引\n",
    "dist, idx = knn.kneighbors(new_user)\n",
    "nearest = X[idx[0][0]]  # 获取最近邻点的坐标，这是一个列表，第一个元素是x坐标，第二个元素是y坐标\n",
    "\n",
    "# 用红线标记新数据点与最近邻点的连接线\n",
    "plt.plot([new_user[0, 0], nearest[0]], [new_user[0, 1], nearest[1]], 'r--')\n",
    "\n",
    "# 为每个点添加坐标文本\n",
    "for x, y in zip(X[:, 0], X[:, 1]):\n",
    "    plt.text(x, y + 0.1, f'({x}, {y})')\n",
    "\n",
    "# 为新数据点添加坐标文本\n",
    "plt.text(new_user[0, 0], new_user[0, 1] + 0.1, f'({new_user[0, 0]}, {new_user[0, 1]})')\n",
    "plt.show()\n",
    "\n",
    "# 输出预测结果\n",
    "if prediction[0] == 0:\n",
    "    print(\"这个用户可能偏好动作片\")\n",
    "else:\n",
    "    print(\"这个用户可能偏好喜剧片\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
